package com.learnyou.server.xingzuo.biz.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.StrUtil;
import com.learnyou.server.xingzuo.biz.IBehaviorParser;
import com.learnyou.server.xingzuo.biz.helper.BehaviorStatisticHelper;
import com.learnyou.server.xingzuo.biz.helper.CosHelper;
import com.learnyou.server.xingzuo.common.config.CosConfig;
import com.learnyou.server.xingzuo.common.constants.BehaviorConstants;
import com.learnyou.server.xingzuo.common.helper.FixCapacityQueue;
import com.learnyou.server.xingzuo.dao.repository.entity.Behavior;
import com.learnyou.server.xingzuo.dao.repository.service.IBehaviorService;
import com.learnyou.server.xingzuo.dto.pet.BehaviorSeparatorDTO;
import com.learnyou.server.xingzuo.dto.pet.BehaviorTimelineDTO;
import com.learnyou.server.xingzuo.dto.pet.CosContentDTO;
import com.learnyou.server.xingzuo.dto.pet.DeviceBehaviorsDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class BehaviorParser implements IBehaviorParser {


    private final CosConfig cosConfig;

    private final CosHelper cosHelper;
    private final IBehaviorService behaviorService;
    private BehaviorStatisticHelper helper;

    @Autowired
    public BehaviorParser(CosConfig cosConfig, IBehaviorService behaviorService) {
        this.cosConfig = cosConfig;
        this.cosHelper = new CosHelper(cosConfig.getSecretId(), cosConfig.getSecretKey(), cosConfig.getRegion()).bucket(cosConfig.getBucketName());
        this.behaviorService = behaviorService;
        this.helper = new BehaviorStatisticHelper(cosConfig.getTimeSeparator());
    }

    @Override
    public void process(){
        List<CosContentDTO> petObjects = cosHelper.getPetObjects();
        petObjects = petObjects.stream().sorted(Comparator.comparingLong(t->getFileTime(t.getKey()))).collect(Collectors.toList());
        BehaviorSeparatorDTO temp = null;
        FixCapacityQueue<Integer> historyBehaviors = new FixCapacityQueue<>(2);
        for (int i = 0; i < petObjects.size(); i++) {
            CosContentDTO cosContentDTO = petObjects.get(i);
            if (i==0){
                //第一个文件需要加载上一个文件的最后两个动作
                List<Integer> lastBehaviors = behaviorService.getLastBehaviors(getFileTime(cosContentDTO.getKey()));
                CollUtil.reverse(lastBehaviors);
                lastBehaviors.forEach(historyBehaviors::add);
            }
            //文件内容解析
            DeviceBehaviorsDTO behaviorsDTO = parseCosContent(cosContentDTO);
            //文件内容错误则丢弃
            if (behaviorsDTO == null || CollUtil.isEmpty(behaviorsDTO.getBehaviors())){
                log.info(cosContentDTO.getKey() + "is invalid");
                continue;
            }
            //首行数据处理，第一行数据时间为时间戳，设为起始时间，时间间隔设为0
            Integer startTime = processStartTime(temp, behaviorsDTO.getBehaviors());
            LinkedList<BehaviorSeparatorDTO> behaviorSeparatorDTOList = helper.statistic(startTime, behaviorsDTO.getBehaviors());

            //如果最后一行时间间隔不足设定时长 5分钟，则记入下一个文件中一起处理，如果当前是最后一个文件，则放弃本次处理
            BehaviorSeparatorDTO last = behaviorSeparatorDTOList.peekLast();
            if (!isValidTimeSeparator(last)){
                if (i==(petObjects.size()-1)){
                    log.info(cosContentDTO.getKey() + "is not complete");
                    break;
                }else {
                    temp = last;
                    behaviorSeparatorDTOList.removeLast();
                }
            }

            //文件内容为空，则跳过
            if (CollUtil.isEmpty(behaviorSeparatorDTOList)){
                log.info(cosContentDTO.getKey() + "is empty");
                continue;
            }
            saveBehaviors(cosContentDTO, behaviorsDTO, behaviorSeparatorDTOList, historyBehaviors);
            moveCosObject(cosContentDTO.getKey());
        }
    }

    /**
     * 处理统计开始时间
     * @param temp 上一个文件中不足5分钟的行为
     * @param behaviors 本次文件中的行为
     */
    private Integer processStartTime(BehaviorSeparatorDTO temp, LinkedList<BehaviorTimelineDTO> behaviors) {
        BehaviorTimelineDTO firstLine = behaviors.peekFirst();
        if (temp == null){
            int startTime = firstLine.getTime();
            firstLine.setTime(0);
            return startTime;
        }else {
            int timeSeparator = firstLine.getTime() - temp.getStartTime();
            if (timeSeparator< cosConfig.getTimeSeparator()){
                //添加上一次的数据为第一行
                behaviors.addFirst(BehaviorTimelineDTO.builder().behavior(temp.getBehaviors()).time(0).build());
                //第二行数据的间隔时间重新计算
                firstLine.setTime(timeSeparator);
            }else {
                //添加上一次的数据为第一行
                behaviors.addFirst(BehaviorTimelineDTO.builder().behavior(temp.getBehaviors()).time(cosConfig.getTimeSeparator()).build());
                firstLine.setTime(0);
            }
            //从上一次数据开始计算时间
            return temp.getStartTime();
        }
    }

    private void saveBehaviors(CosContentDTO cosContentDTO
            , DeviceBehaviorsDTO behaviorsDTO
            , List<BehaviorSeparatorDTO> behaviorSeparatorDTOList
            , FixCapacityQueue<Integer> historyBehaviors) {
        behaviorService.removeByBucketKey(cosContentDTO.getKey());
        //汇总获取每组中的次数最多的行为
        List<Behavior> behaviors = new ArrayList<Behavior>();
        for (BehaviorSeparatorDTO separatorDTO: behaviorSeparatorDTOList) {
            Integer maxCountBehavior = helper.getMaxCountBehavior(separatorDTO.getBehaviors());
            Integer resultBehavior = processSleepBehavior(maxCountBehavior, historyBehaviors);
            behaviors.add(new Behavior()
                    .setCode(behaviorsDTO.getDeviceNo())
                    .setStartTime(DateUtil.date(separatorDTO.getStartTime()*1000l))
                    .setEndTime(DateUtil.date(separatorDTO.getEndTime()*1000l))
                    .setBehavior(resultBehavior)
                    .setBucketKey(cosContentDTO.getKey())
                    .setRefBehaviors(separatorDTO.getBehaviors().stream().sorted().map(Object::toString).collect(Collectors.joining()))
            )
            ;
        }
        behaviorService.saveBatch(behaviors);
    }

    private Integer processSleepBehavior(Integer currentBehavior, FixCapacityQueue<Integer> historyBehaviors) {
        if (currentBehavior == BehaviorConstants.REST && historyBehaviors.isNotEmpty()){
            Integer prev = historyBehaviors.poll();
            Integer prevPrev = historyBehaviors.poll();
            historyBehaviors.add(prev);
            historyBehaviors.add(currentBehavior);
            if (isSleep(prev)){
                if (isSleep(prevPrev)){
                    return BehaviorConstants.DEEP_SLEEP;
                }else {
                    return BehaviorConstants.SLEEP;
                }
            }
        }
        historyBehaviors.add(currentBehavior);
        return currentBehavior;
    }

    private boolean isSleep(Integer behavior) {
        return behavior!=null && (behavior==BehaviorConstants.REST || behavior==BehaviorConstants.SLEEP || behavior==BehaviorConstants.DEEP_SLEEP);
    }

    private boolean isValidTimeSeparator(BehaviorSeparatorDTO dto) {
        return (dto.getEndTime() - dto.getStartTime()) >= cosConfig.getTimeSeparator();
    }

    public static Long getFileTime(String key){
        String time = StrUtil.sub(key, key.indexOf("/") + 1, key.indexOf("."));
        return Convert.toLong(time);
    }
    private void moveCosObject(String key) {
        cosHelper.move(key, cosConfig.getFinishedBucketName());
    }

    /**
     * 解析文件内容，过滤格式错误的行
     * @param petDTO
     * @return
     */
    private DeviceBehaviorsDTO parseCosContent(CosContentDTO petDTO) {
        String deviceNo = parseDeviceNo(petDTO.getKey());
        if (StrUtil.isEmpty(deviceNo)){
            return null;
        }
        DeviceBehaviorsDTO behaviorsDTO = new DeviceBehaviorsDTO();
        behaviorsDTO.setDeviceNo(deviceNo);
        LinkedList<BehaviorTimelineDTO> timelineDTOs = new LinkedList<>();
        petDTO.getLines().stream().filter(this::filterValidLine).forEach(line->{
            String[] columns = StrUtil.splitToArray(line, ',');
            if (columns.length==2){
                Integer times = Convert.toInt(columns[0]);
                Integer behavior = Convert.toInt(columns[1]);
                if (behavior != null && times != null){
                    timelineDTOs.add(BehaviorTimelineDTO.builder().behavior(CollUtil.newArrayList(behavior)).time(times).build());
                }
            }
        });
        behaviorsDTO.setBehaviors(timelineDTOs);
        if (CollUtil.isEmpty(timelineDTOs)){
            return null;
        }
        BehaviorTimelineDTO first = timelineDTOs.get(0);
        //第一行不是时间戳，不符合要求的文件
        if (first.getTime()<1000){
            return null;
        }
        return behaviorsDTO;
    }

    /**
     * 校验行是否合法
     * TODO: 加入正则匹配，规范格式
     * @param line
     * @return
     */
    private boolean filterValidLine(String line) {
        return StrUtil.isNotEmpty(line)&&line.indexOf(',')>0;
    }

    private String parseDeviceNo(String key) {
        if (StrUtil.isEmpty(key)||StrUtil.indexOf(key, '/')== CharSequenceUtil.INDEX_NOT_FOUND) {
            return null;
        }
        return StrUtil.sub(key, 0, StrUtil.indexOf(key, '/'));
    }
}
